(*****************************************************************************
 * Delphi Plug-in Framework (DPF)
 * Copyright (C) 2006-2007 Tao Yuan
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the
 * License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.                                                               }
 *
 * The Initial Developer of the Original Code is Tao Yuan. Portions created by
 * Tao Yuan are Copyright (C) Tao Yuan (taoy.cn att gmail dott com)
 * All rights reserved.
 *****************************************************************************)
 
unit RDExtensionImpl;

interface

uses
  Rcl,
  RDPluginRegistry, RDPluginRegistryImpl, RDModel, RDExceptions, RdClasses,
  RDConsts, RDPathResolver, RDIntegrityCheckReport, RDIntegrityChecker;

type
  TExtensionImpl = class;

  TParameterImpl = class(TPluginElementImpl, IParameter)
  private
    modelParam: TModelParameter;
    isParamValid: Boolean;
    valueVar: Variant;
    valueIntf: IInterface;
    valueObj: TObject;
    subParameters: IList;
    definition: IParameterDefinition;
    definitionDetected: Boolean;
    superParameter: TParameterImpl;
    extension: TExtensionImpl;
    flagParamValid: Boolean;
    function getExtensionPoint(uniqueId: string): IExtensionPoint; overload;
    function getExtensionPoint(pluginId, pointId: string): IExtensionPoint;
        overload;
  protected
    destructor Destroy; override;
    
  public
    constructor Create(aExtension: TExtensionImpl; aSuperParameter: TParameterImpl;
        aModel: TModelParameter);  
    (**
     * @return extension this parameter belongs to
     *)
    function getDeclaringExtension: IExtension; stdcall;
    function getDeclaringPluginDescriptor: IPluginDescriptor; stdcall;
    function getDeclaringPluginFragment: IPluginFragment; stdcall;
    (**
     * Returns definition for this extension parameter.
     * May return <code>null</code> for "invalid" parameters.
     * @return parameter definition or <code>null</code>, if this parameter
     *         is "invalid"
     *)
    function getDefinition: IParameterDefinition;stdcall;
    (**
     * Returns sub-parameter with given ID or <code>null</code> if no
     * sub-parameters exist. If more than one sub-parameters with given ID
     * found, the method should throw an {@link IllegalArgumentException}.
     * @param id ID of sub-parameter to look for
     * @return sub-parameter with given ID
     *)
    function getSubParameter(id: String): IParameter; stdcall;
    (**
     * Returns collection of all sub-parameters defined in this parameter.
     * @return collection of {@link Extension.Parameter} objects
     *)
    function getSubParameters: IIntfCollection; overload; stdcall;
    (**
     * @param id ID of sub-parameter to look for
     * @return collection of all sub-parameters with given ID
     *)
    function getSubParameters(id: String): IIntfCollection; overload;  stdcall;
    (**
     * @return parameter, of which this one is child or <code>null</code> if
     *         this is top level parameter
     *)
    function getSuperParameter: IParameter; stdcall;
    function isValid: Boolean; stdcall;
    function parseValue: Boolean; stdcall;
    (**
     * @return parameter value as it is specified in manifest, if no value
     *         provided there, this method should return empty string
     *)
    function rawValue: String; stdcall;
    function toString: string; stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_BOOLEAN}, this method
     * should throw an {@link UnsupportedOperationException}.
     * @return value as Boolean object
     *)
    function valueAsBoolean: Boolean; stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type {@link ExtensionPoint.ParameterDefinition#TYPE_DATE},
     * {@link ExtensionPoint.ParameterDefinition#TYPE_TIME}
     * or {@link ExtensionPoint.ParameterDefinition#TYPE_DATETIME},
     * this method should throw an {@link UnsupportedOperationException}.
     * @return value as Date object
     *)
    function valueAsDate: TDateTime; stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_EXTENSION_ID}, this
     * method should throw an {@link UnsupportedOperationException}.
     * @return value as Extension object
     *)
    function valueAsExtension: IExtension;stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_EXTENSION_POINT_ID},
     * this method should throw an {@link UnsupportedOperationException}.
     * @return value as ExtensionPoint object
     *)
    function valueAsExtensionPoint: IExtensionPoint; stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_NUMBER}, this method
     * should throw an {@link UnsupportedOperationException}.
     * @return value as Number object
     *)
     
    //Number valueAsNumber;
    function valueAsNumber: Integer; stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_PLUGIN_ID}, this
     * method should throw an {@link UnsupportedOperationException}.
     * @return value as PluginDescriptor object
     *)
    function valueAsPluginDescriptor: IPluginDescriptor;stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_STRING}, this method
     * should throw an {@link UnsupportedOperationException}.
     * @return value as String object
     *)
    function valueAsString: String; stdcall;
    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_RESOURCE}, this
     * method should throw an {@link UnsupportedOperationException}.
     * @return value as absolute or relative URL as specified in manifest
     *)
    function valueAsUrl: string; overload; stdcall;

    (**
     * Returns "typed" value of parameter. If this parameter is invalid or
     * is not of type
     * {@link ExtensionPoint.ParameterDefinition#TYPE_RESOURCE}, this
     * method should throw an {@link UnsupportedOperationException}.
     * @param pathResolver path resolver to make URL absolute
     * @return value as absolute URL
     *)
    function valueAsUrl(pathResolver: IPathResolver): string; overload; stdcall;
  end;

  TExtensionImpl = class(TPluginElementImpl, IExtension)
  private
    bValid: Boolean;
    flagValidate: Boolean;
//    log: TLogger;
    parameters: IList;
    model: TModelExtension;
    function getExtensionPoint(uniqueId: string): IExtensionPoint; overload;
    function getExtensionPoint(pluginId, pointId: string): IExtensionPoint;
        overload;
    function validateParameters(allDefinitions, allParams: IIntfCollection):
        IIntfCollection; overload;
    function validateParameters(def: IParameterDefinition; params:
        IIntfCollection): IIntfCollection; overload;
  protected

    (**
     * @return ID of plug-in, extended point belongs to
     *)
    function getExtendedPluginId: String; stdcall;
    (**
     * @return ID of extended point
     *)
    function getExtendedPointId: String; stdcall;
    (**
     * Returns top level parameter with given ID or <code>null</code> if no top
     * level parameters exist. If more than one top level parameters with given
     * ID found, the method should throw an {@link IllegalArgumentException}.
     * @param id ID of parameter to look for
     * @return top level parameter with given ID
     *)
    function getParameter(id: String): IParameter; stdcall;
    (**
     * Returns collection of all top level parameters defined in this extension.
     * @return collection of {@link Extension.Parameter} objects
     *)
    function getParameters: IIntfCollection; overload; stdcall;
    (**
     * @param id ID of parameter to look for
     * @return collection of all top level parameters with given ID
     *)
    function getParameters(id: String): IIntfCollection; overload;stdcall;
    (**
     * Returns combination of plug-in ID and extension ID that is unique
     * within whole set of plug-ins, available for activation.
     * @return unique ID for this extension
     *)
    function getUniqueId: String; stdcall;
    (**
     * @return <code>true</code> if extension is considered to be valid
     *)
    function isValid: Boolean; stdcall;
    function validate: IIntfCollection; stdcall;
  public
    constructor Create(descr: IPluginDescriptor; aFragment: IPluginFragment;
        aModel: TModelExtension);
    destructor Destroy; override;
    procedure registryChanged;
  end;


implementation                                        

uses
  SysUtils, RclSysUtils;

constructor TParameterImpl.Create(aExtension: TExtensionImpl; aSuperParameter:
    TParameterImpl; aModel: TModelParameter);
var
  it: IIterator;
begin
  extension := aExtension;
  inherited Create(extension.getDeclaringPluginDescriptor,
    extension.getDeclaringPluginFragment, aModel.id, aModel.documentation);
  superParameter := aSuperParameter;
  modelParam := aModel;
  subParameters := TArrayList.Create(modelParam.params.Size);
  it := modelParam.params.First;
  while it.hasNext do
    subParameters.Add(TParameterImpl.Create(extension, Self,
            TModelParameter(it.next)));
//  subParameters := Collections.unmodifiableList(subParameters);
  log.debug('object instantiated: ' + Self.toString); //$NON-NLS-1$

end;

destructor TParameterImpl.Destroy;
begin
  RclSysUtils.NilNoRelease(definition);
  inherited;
end;

function TParameterImpl.getDeclaringExtension: IExtension;
begin
  Supports(extension, IExtension, Result);
end;

function TParameterImpl.getDeclaringPluginDescriptor: IPluginDescriptor;
begin
  Result := extension.getDeclaringPluginDescriptor;
end;

function TParameterImpl.getDeclaringPluginFragment: IPluginFragment;
begin
  Result := extension.getDeclaringPluginFragment;
end;

function TParameterImpl.getDefinition: IParameterDefinition;
var
  definitions: IIntfCollection;
  it: IIntfIterator;
  def: IParameterDefinition;
  obj: TRdLogPersistent;
begin
  if (definitionDetected) then
  begin
      Result := definition;
      Exit;
  end;
  definitionDetected := true;
  log.debug('detecting definition for parameter ' + Self.toString); //$NON-NLS-1$
  if (superParameter <> nil) then
  begin
      if (superParameter.getDefinition = nil) then
      begin
          Result := nil;
          Exit;
      end;
      if Rcl.Str.IsEqual(TYPE_ANY, superParameter.getDefinition.getType) then
      begin
          definition := superParameter.getDefinition;
          log.debug('definition detected - '{ + definition}); //$NON-NLS-1$
          Result := definition;
          Exit;
      end;
      definitions :=
          superParameter.getDefinition.getSubDefinitions;
  end else
      definitions := extension.getExtensionPoint(
                  getDeclaringExtension.getExtendedPluginId,
                  getDeclaringExtension.getExtendedPointId).
                      getParameterDefinitions;

  it := definitions.First;
  while it.HasNext do
  begin
    Supports(it.next, IParameterDefinition, def);
    if Rcl.Str.IsEqual(def.getId, Self.getId) then
    begin
        definition := def;
        break;
    end;
  end;


  log.debug('definition detected - '{ + definition}); //$NON-NLS-1$
  Result := definition;
end;

function TParameterImpl.getExtensionPoint(uniqueId: string): IExtensionPoint;
begin
  Result := extension.getExtensionPoint(uniqueId);
end;

function TParameterImpl.getExtensionPoint(pluginId, pointId: string):
    IExtensionPoint;
begin
  Result := extension.getExtensionPoint(pluginId, pointId);
end;

function TParameterImpl.getSubParameter(id: String): IParameter;
var
  param,
  resultParam: TParameterImpl;
  it: IIterator;
begin
  it := subParameters.First;
  while (it.hasNext) do
  begin
      param := TParameterImpl(it.next);
      if Rcl.Str.IsEqual(param.getId, id) then
      begin
          if (resultParam = nil) then
              resultParam := param
          else
              raise EIllegalArgument.Create(
                  'more than one parameter with ID ' + id //$NON-NLS-1$
                  + ' defined in extension ' + getUniqueId); //$NON-NLS-1$
      end;
  end;
  Result := resultParam;
end;

function TParameterImpl.getSubParameters: IIntfCollection;
begin
  Result := Collections.ToIntfCollection(subParameters);
end;

function TParameterImpl.getSubParameters(id: String): IIntfCollection;
var
//  list: IList;
  it: IIterator;
  param: TParameterImpl;
begin
  Result := TIntfLinkedList.Create;//(nil, False);
  it := subParameters.First;
  while it.HasNext do
  begin
    param := TParameterImpl(it.next);
    if Rcl.Str.IsEqual(param.getId, id) then
      Result.add(param);
  end;
//  Result := Collections.unmodifiableList(list);
end;

function TParameterImpl.getSuperParameter: IParameter;
begin
  Result := superParameter;
end;

function TParameterImpl.isValid: Boolean;
begin
  if (flagParamValid) then
  begin
    Result := isParamValid;
    Exit;
  end;
  log.debug('validating parameter ' + Self.toString); //$NON-NLS-1$
  isParamValid := False;
  if (getDefinition = nil) then
  begin
    isParamValid := FALSE;
    log.warn('can''t detect definition for parameter ' + Self.toString); //$NON-NLS-1$
  end else
  begin
    if modelParam.Value <> '' then
    begin
       isParamValid := parseValue
//       valueVar := 'test';
//       isParamValid := True;
    end
    else
      isParamValid := True;
  end;
  Result := isParamValid;
  flagParamValid := True;
end;

function TParameterImpl.parseValue: Boolean;
var
  val: string;
  customExtPoint: IExtensionPoint;
  ext: IExtension;
  extId: string;  
  extPoint: IExtensionPoint;
  extPointUid: string;
  it: IIntfIterator;
  s: string;
  st: string;
  I: Integer;
begin
  // note that definition already detected and is not NULL
  log.debug('parsing value for parameter ' + Self.toString); //$NON-NLS-1$
  if Rcl.Str.IsEqual(TYPE_ANY, definition.getType)
      or Rcl.Str.IsEqual(TYPE_NULL, definition.getType) then
  begin
      Result := TRUE;
      Exit;
  end;
  
  if Rcl.Str.IsEqual(TYPE_STRING, definition.getType) then
  begin
      valueVar := modelParam.Value;
      Result := TRUE;
      Exit;
  end;

  val := Trim(modelParam.Value);
  if (val = '') then
  begin
      Result := TRUE;
      Exit;
  end;

  if Rcl.Str.IsEqual(TYPE_BOOLEAN, definition.getType) then
  begin
    if Rcl.Str.IsEqual(val, 'True') then
      valueVar := True
    else if Rcl.Str.IsEqual(val, 'False') then
      valueVar := False
    else
    begin
          log.error('can''t parse value ' + val //$NON-NLS-1$
                  + ' of parameter ' + Self.toString); //$NON-NLS-1$
          Result := FALSE;
    end;  
  end
  else if Rcl.Str.IsEqual(TYPE_NUMBER, definition.getType) then
  begin
    try
      valueVar := StrToInt(val)
    except
      log.error('can''t parse value ' + val //$NON-NLS-1$
              + ' of parameter ' + Self.toString); //$NON-NLS-1$
      Result := False;
      Exit;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_DATE, definition.getType) then
  begin
    try
      valueVar := StrToDate(val);
    except
      log.error('can''t parse value ' + val //$NON-NLS-1$
              + ' of parameter ' + Self.toString); //$NON-NLS-1$
      Result := False;
      Exit;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_TIME, definition.getType) then
  begin
    try
      valueVar := StrToTime(val);
    except
      log.error('can''t parse value ' + val //$NON-NLS-1$
              + ' of parameter ' + Self.toString); //$NON-NLS-1$
      Result := False;
      Exit;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_DATETIME, definition.getType) then
  begin
    try
      valueVar := StrToDateTime(val);
    except
      log.error('can''t parse value ' + val //$NON-NLS-1$
              + ' of parameter ' + Self.toString); //$NON-NLS-1$
      Result := False;
      Exit;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_PLUGIN_ID, definition.getType) then
  begin
    try
      valueIntf := getDeclaringPluginDescriptor.getRegistry
         .getPluginDescriptor(val);
    except
      log.error('unknown plug-in ID ' + val //$NON-NLS-1$
              + ' provided in parameter ' + Self.toString); //$NON-NLS-1$
      Result := False;
      Exit;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_EXTENSION_POINT_ID, definition.getType) then
  begin

    valueIntf := getExtensionPoint(val);
    if valueIntf = nil then
    begin
      log.error('unknown plug-in ID ' + val //$NON-NLS-1$
              + ' provided in parameter ' + Self.toString); //$NON-NLS-1$
      Result := False;
      Exit;
    end;
    
    if (definition.getCustomData <> '') then
    begin
        customExtPoint := getExtensionPoint(definition.getCustomData);
        if (customExtPoint = nil) then
        begin
            log.error('unknown extension point UID ' //$NON-NLS-1$
                    + definition.getCustomData() //$NON-NLS-1$
                    + ' provided as custom data in parameter ' //$NON-NLS-1$
                    + Self.toString);
            Result := False;
            Exit;
        end;

        if (not(IExtensionPoint(valueIntf).isSuccessorOf(customExtPoint))) then
        begin
          log.error('extension point with UID ' + val //$NON-NLS-1$
                  + ' provided in parameter ' + Self.toString //$NON-NLS-1$
                  + ' doesn''t ''inherit'' point that is defined' //$NON-NLS-1$
                  + ' according to custom data in parameter' //$NON-NLS-1$
                  + ' definition - ' //$NON-NLS-1$
                  + definition.getCustomData);
          Result := False;
          Exit;
        end;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_EXTENSION_ID, definition.getType) then
  begin
    extId := getDeclaringPluginDescriptor.getRegistry.extractId(val);
    it := (getDeclaringPluginDescriptor
      .getRegistry.getPluginDescriptor(
              getDeclaringPluginDescriptor.getRegistry
              .extractPluginId(val)).getExtensions as IIntfCollection).First;
    while it.hasNext do
    begin
        ext := it.next as IExtension;
        if Rcl.Str.IsEqual(ext.getId, extId) then
        begin
            valueIntf := ext;
            break;
        end;
    end;
    if (valueIntf = nil) then
    begin
      log.error('unknown plug-in ID ' + val //$NON-NLS-1$
              + ' provided in parameter ' + Self.toString); //$NON-NLS-1$
          Result := False;
          Exit;
    end;
    if (definition.getCustomData <> '') then
    begin 
      customExtPoint := getExtensionPoint(definition.getCustomData);
      if customExtPoint = nil then
      begin
        log.error('unknown extension point UID ' //$NON-NLS-1$
                + definition.getCustomData //$NON-NLS-1$
                + ' provided as custom data in parameter ' //$NON-NLS-1$
                + Self.toString);
        Result := False;
        Exit;
      end;
      extPointUid :=
          getDeclaringPluginDescriptor.getRegistry
          .makeUniqueId((valueIntf as IExtension).getExtendedPluginId,
                  (valueIntf as IExtension).getExtendedPointId);
      extPoint := getExtensionPoint(extPointUid);
      if extPoint = nil then 
      begin
        log.error('extension point ' + extPointUid //$NON-NLS-1$
                + ' is unknown for extension ' //$NON-NLS-1$
                + (IExtension(valueIntf)).getUniqueId
                + ', provided in parameter ' + Self.toString); //$NON-NLS-1$
        Result := False;
        Exit;
      end;
      if (extPoint <> customExtPoint)
              and (not extPoint.isSuccessorOf(customExtPoint)) then
      begin
          log.error('extension with UID ' + val //$NON-NLS-1$
                  + ' provided in parameter ' + Self.toString //$NON-NLS-1$
                  + ' extends point that not allowed according' //$NON-NLS-1$
                  + ' to custom data defined in parameter' //$NON-NLS-1$
                  + ' definition - ' //$NON-NLS-1$
                  + definition.getCustomData);

          Result := False;
          Exit;
      end;
    end;
  end
  else if Rcl.Str.IsEqual(TYPE_FIXED, definition.getType) then
  begin
    I := 0;
    s := definition.getCustomData;
    while s <> '' do
    begin
      st := Rcl.Str.StrToken(S, '|');//StrToken(s, '|');
      if Rcl.Str.IsEqual(Trim(st), val) then
      begin
        valueVar := val;
        Result := True;
        Exit;
      end;
    end;
    log.error('not allowed value ' + val //$NON-NLS-1$
            + ' provided in parameter ' + Self.toString); //$NON-NLS-1$
    Result := False;
    Exit;
  end
  else if Rcl.Str.IsEqual(TYPE_RESOURCE, definition.getType) then
  begin
      try
          //valueIntf := SilUrl.Url.Create(val);//SilUrl.IURL.Create(val);
          valueVar := val;
      except
//        on emu: EMalformedURL do
        begin
          log.debug('can''t parse value ' + val //$NON-NLS-1$
                  + ' of parameter ' + Self.toString
                  + ' as an absolute URL, we''ll treat it as relative' //$NON-NLS-1$
                  + ' URL'); //$NON-NLS-1$
          //return Boolean.FALSE;
          valueVar := '';
        end;
      end;
      Result := True;
      Exit;
  end;
  Result := True;
end;

function TParameterImpl.rawValue: String;
begin
  Result := modelParam.Value;
end;

function TParameterImpl.toString: string;
begin
  Result := '{PluginExtension.Parameter: extUid=' //$NON-NLS-1$
        + getDeclaringExtension.getUniqueId + '; id=' + getId //$NON-NLS-1$
        + '}'; //$NON-NLS-1$;
end;

function TParameterImpl.valueAsBoolean: Boolean;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_BOOLEAN, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_BOOLEAN);
              
  Result := valueVar;
end;

function TParameterImpl.valueAsDate: TDateTime;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if (not Rcl.Str.IsEqual(TYPE_DATE, definition.getType))
      and (not Rcl.Str.IsEqual(TYPE_DATETIME, definition.getType)
      and (not Rcl.Str.IsEqual(TYPE_TIME, definition.getType))) then
    raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
            + TYPE_DATE + ' nor'
            + TYPE_DATETIME + ' nor'
            + TYPE_TIME);

  Result := valueVar;
end;

function TParameterImpl.valueAsExtension: IExtension;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_EXTENSION_ID, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_EXTENSION_ID);

  Supports(valueIntf, IExtensionPoint, Result);
//  Result := valueIntf as IExtension;
end;

function TParameterImpl.valueAsExtensionPoint: IExtensionPoint;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_EXTENSION_POINT_ID, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_EXTENSION_POINT_ID);

  Supports(valueIntf, IExtensionPoint, Result);
//  Result := valueIntf as IExtensionPoint;
end;

function TParameterImpl.valueAsNumber: Integer;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_NUMBER, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_NUMBER);

  Result := valueVar;  
end;

function TParameterImpl.valueAsPluginDescriptor: IPluginDescriptor;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_PLUGIN_ID, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_PLUGIN_ID);

  Supports(valueIntf, IPluginDescriptor, Result);
//  Result := valueIntf as IPluginDescriptor;
end;

function TParameterImpl.valueAsString: String;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_STRING, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_STRING);

  Result := valueVar;
end;

function TParameterImpl.valueAsUrl: string;
begin
  valueAsUrl(nil);
end;

function TParameterImpl.valueAsUrl(pathResolver: IPathResolver): string;
begin
  if (not isValid) then
    raise EUnsupportedOperationException.Create(
      'parameter value is invalid');

  if not Rcl.Str.IsEqual(TYPE_RESOURCE, definition.getType) then
      raise EUnsupportedOperationException.Create('parameter type is not ' //$NON-NLS-1$
              + TYPE_RESOURCE);

  if (pathResolver = nil) or (valueVar <> '') then
    Result := valueVar
  else
    Result := pathResolver.resolvePath(getDeclaringPluginDescriptor(),
                    rawValue());
end;

constructor TExtensionImpl.Create(descr: IPluginDescriptor; aFragment:
    IPluginFragment; aModel: TModelExtension);
var
  it: IIterator;
  param: TParameterImpl;
begin
  inherited Create(descr, aFragment, aModel.id, aModel.documentation);
  model := aModel;
  if (model.pluginId = '') then
      raise EManifestProcessing.Create(
              'dpf.plugin.registry.xml', //$NON-NLS-1$
              'extensionIdIsBlank', descr.getId); //$NON-NLS-1$

  if (model.pointId = '') then
      raise EManifestProcessing.Create(
              'dpf.plugin.registry.xml', //$NON-NLS-1$
              'extendedPointIdIsBlank', descr.getId); //$NON-NLS-1$
  
  parameters := TArrayList.Create(model.params.Size, False);
  it := model.params.First;
  while it.HasNext do
  begin
    param := TParameterImpl.Create(Self, nil, TModelParameter(it.next));
//    param.Free;
    parameters.Add(param);
  end;

//  parameters := Collections.unmodifiableList(parameters);
  log.debug('object instantiated: ' + Self.toString); //$NON-NLS-1$
end;

destructor TExtensionImpl.Destroy;
var
  it: IIterator;
  param: TParameterImpl;
begin
  it := parameters.First;
  while it.HasNext do
  begin
    param := TParameterImpl(it.Next);
    try
      param.Free;
    except

    end;
  end;
  inherited;
  // TODO -cMM: TExtensionImpl.Destroy default body inserted
end;

function TExtensionImpl.getExtendedPluginId: String;
begin
  Result := model.PluginId;
end;

function TExtensionImpl.getExtendedPointId: String;
begin
  Result := model.PointId;
end;

function TExtensionImpl.getExtensionPoint(uniqueId: string): IExtensionPoint;
var
  registry: IPluginRegistry;
begin
  registry := getDeclaringPluginDescriptor().getRegistry();
  Result := getExtensionPoint(registry.extractPluginId(uniqueId),
          registry.extractId(uniqueId));
end;

function TExtensionImpl.getExtensionPoint(pluginId, pointId: string):
    IExtensionPoint;
var
  registry: IPluginRegistry;
  it: IIntfIterator;
  point: IExtensionPoint;
begin
  registry := getDeclaringPluginDescriptor().getRegistry();
  if (not registry.isPluginDescriptorAvailable(pluginId)) then
      Exit; //return nil;

  it := registry.getPluginDescriptor(pluginId).getExtensionPoints.First;
  while it.HasNext do
  begin
    Supports(it.Next, IExtensionPoint, point);
    if Rcl.Str.IsEqual(point.getId, pointId) then
    begin
      Result := point;
      Exit;
    end;
  end;
end;

function TExtensionImpl.getParameter(id: String): IParameter;
var
  resultParam,
  param: TParameterImpl;
  it: IIterator;
begin
  resultParam := nil;
  it := parameters.First;
  while it.HasNext do
  begin
    param := TParameterImpl(it.Next);
    if Rcl.Str.IsEqual(id, param.getId) then
    begin
      if resultParam = nil then
        resultParam := param
      else
        raise EIllegalArgument.Create('more than one parameter with ID ' + id //$NON-NLS-1$
                  + ' defined in extension ' + getUniqueId);
    end;
  end;
  Result := resultParam;
end;

function TExtensionImpl.getParameters: IIntfCollection;
begin
  //Result := parameters;
  Result := Collections.ToIntfCollection(parameters);
end;

function TExtensionImpl.getParameters(id: String): IIntfCollection;
var
  listParam: IIntfList;
  param: IParameter;
  it: IIterator;
begin
  listParam := TIntfLinkedList.Create;
  it := parameters.First;
  while it.hasNext do
  begin
//      param := TParameterImpl(it.next);
    Supports(it.Next, IParameter, param);
    if Rcl.Str.IsEqual(param.getId, id) then
        listParam.add(param);
  end;
  Result := Collections.unmodifiableList(listParam);
end;

function TExtensionImpl.getUniqueId: String;
begin
  Result := getDeclaringPluginDescriptor.getRegistry.makeUniqueId(
          getDeclaringPluginDescriptor.getId, getId);
end;

function TExtensionImpl.isValid: Boolean;
begin
  if not flagValidate then
      validate;
  Result := bValid;
end;

procedure TExtensionImpl.registryChanged;
begin
  flagValidate := False;
end;

function TExtensionImpl.validate: IIntfCollection;
var
  point: IExtensionPoint;
begin
  flagValidate := True;
  point := getExtensionPoint(getExtendedPluginId(), getExtendedPointId());

  if point = nil then
  begin
      bValid := FALSE;
      Result := Collections.singletonList(
              TReportItemImpl.Create(
                  SEVERITY_ERROR, Self,
                  ERROR_INVALID_EXTENSION,
                  'extPointNotAvailable', [ //$NON-NLS-1$
                  getDeclaringPluginDescriptor.getRegistry
                      .makeUniqueId(getExtendedPluginId,
                              getExtendedPointId), getUniqueId]) as IInterface);
      Exit;
  end;

  Result := validateParameters(point.getParameterDefinitions(),
      Collections.ToIntfCollection(parameters));

//  validateParameters(
//          getDeclaringPluginDescriptor.getRegistry.
//          getExtensionPoint(getExtendedPluginId, getExtendedPointId)
//              .getParameterDefinitions, parameters);

  bValid := Result.IsEmpty;
//  bValid := result.isEmpty ? Boolean.TRUE : Boolean.FALSE;
end;

function TExtensionImpl.validateParameters(allDefinitions, allParams:
    IIntfCollection): IIntfCollection;
var
  paramList: IIntfList;
  groups: IStrIntfMap;
  it: IIntfIterator;
  param: IParameter;
//  paramObj: TObject;
  def: IParameterDefinition;
  strList: IStrList;
  paramGroup: IIntfCollection;
begin
  //List result = new LinkedList;
//  paramList := TLinkedList.Create;
  paramList := TIntfLinkedList.Create;
  //Map groups = new HashMap;
  groups := TStrIntfHashMap.Create;
  it := allParams.First;
  while it.HasNext do
  begin
//    paramObj := it.next;
    Supports(it.next, IParameter, param);
    def := param.getDefinition;
    if def = nil then
    begin
      strList := TStrArrayList.Create;
      strList.Add(param.getId);
      strList.Add(getUniqueId);
      paramList.Add(TReportItemImpl.Create(
              SEVERITY_ERROR, Self,
              ERROR_INVALID_EXTENSION,
              'cantDetectParameterDef',strList));
      Continue;        
    end;
    if (groups.containsKey(param.getId)) then
      (groups.GetValue(param.getId) as IIntfCollection).Add(param)
    else
    begin
      paramGroup := TIntfLinkedList.Create;//(nil, False);
      paramGroup.add(param);
      groups.PutValue(param.getId, paramGroup);
    end;    
  end;
  
  if (not paramList.isEmpty) then
  begin
    Result := paramList;
    Exit;
  end;

  it := allDefinitions.First;
  while it.HasNext do
  begin
//    it.Next, IParameterDefinition, def);
    Supports(it.Next, IParameterDefinition, def);
    paramGroup := groups.GetValue(def.getId) as IIntfCollection;
    if paramGroup = nil then
      paramGroup := TIntfLinkedList.Create;
    paramList.AddAll(validateParameters(def, paramGroup));
  end;

  Result := paramList;
end;

function TExtensionImpl.validateParameters(def: IParameterDefinition; params:
    IIntfCollection): IIntfCollection;
var
  paramResult: IIntfList;
  count: Integer;
  it: IIntfIterator;
  param: IParameter;
  paramImpl: TParameterImpl;
begin
  log.debug('validating parameters for definition ' + def.getType); //$NON-NLS-1$
  if Rcl.Str.IsEqual(MULT_ONE, def.getMultiplicity) and (params.size <> 1) then
  begin
      Result := Collections.singletonList(
              TReportItemImpl.Create(
                  SEVERITY_ERROR, Self,
                  ERROR_INVALID_EXTENSION,
                  'tooManyOrFewParams', [ //$NON-NLS-1$
                  def.getId, getUniqueId]) as IInterface);
      Exit;
  end
  else if Rcl.Str.IsEqual(MULT_NONE_OR_ONE,
          def.getMultiplicity) and (params.size > 1) then
  begin
      Result := Collections.singletonList(
              TReportItemImpl.Create(
                  SEVERITY_ERROR, Self,
                  ERROR_INVALID_EXTENSION,
                  'tooManyParams', [ //$NON-NLS-1$
                  def.getId, getUniqueId]) as IInterface);
      Exit;
  end
  else if Rcl.Str.IsEqual(MULT_ONE_OR_MORE,
          def.getMultiplicity) and params.isEmpty then
  begin
      Result := Collections.singletonList(
              TReportItemImpl.Create(
                  SEVERITY_ERROR, Self,
                  ERROR_INVALID_EXTENSION,
                  'tooFewParams', [ //$NON-NLS-1$
                          def.getId, getUniqueId]) as IInterface);
      Exit;
  end;
  if (params.isEmpty) then
  begin
    Result := nil;//Collections.EMPTY_LIST;
    Exit;
  end;
//  paramResult := TLinkedList.Create;
  paramResult := TIntfLinkedList.Create;
  count := 1;
  it := params.First;
  while it.HasNext do
  begin
//    param := TParameterImpl(it.Next);
    Supports(it.Next, IParameter, param);
    paramImpl := TParameterImpl(param.Implementor);
    if not paramImpl.isValid then
      paramResult.Add(TReportItemImpl.Create(
                  SEVERITY_ERROR, Self,
                  ERROR_INVALID_EXTENSION,
                  'invalidParameterValue', [ //$NON-NLS-1$
                def.getId, IntToStr(count), getUniqueId]));
    if (not Rcl.Str.IsEqual(TYPE_ANY, def.getType))
            and paramResult.isEmpty then
      paramResult.AddAll(validateParameters(
              param.getDefinition.getSubDefinitions,
              param.getSubParameters));
    Inc(count);
  end;  
  Result := paramResult;
end;



end.
